package cn.android.support.v7.lib.sin.crown.utils;

import android.app.Activity;
import android.os.Build;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.net.HttpURLConnection;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.android.support.v7.lib.sin.crown.dialog.ProgressDailog;

/**
 * 纯java的Http请求，不需要任何第三方库的支持。也不需要android 6.0之后的org.apache.http.legacy.jar(确认不需要)
 * 如果发现url无法访问，那是因为服务器为了安全性，对传参的形式进行了严格规定【header,params,body,以及是否加密】。如果没有符合要求访问，就会返回错误请求(显示该url无法访问)。
 * Created by 彭治銘 on 2017/5/23.
 */

public class HttpUtils {
    private static HttpUtils http;
    private ThreadPoolExecutor threadPoolExecutor;
    private int timeOut = 1500;//超时链接时间，单位毫秒,一般500毫秒足已。亲测100%有效。极少数设备可能脑抽无效。不用管它。

    private HttpUtils() {
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        int maxinumPoolSize = corePoolSize * 2 + 1;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>();
        threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxinumPoolSize, keepAliveTime, unit, workQueue);
        //Log.e("test", "线程池核心数量:\t" + corePoolSize);
    }


    /**
     * 初始化
     *
     * @return
     */
    public static HttpUtils getInstance() {
        if (http == null) {
            http = new HttpUtils();
        }
        return http;
    }


    //与服务器json格式相对应。基本实体类。格式基本大多都是是这样。
    public static class BaseBean implements Serializable {
        private String result;//返回结果，一般判断是否正确返回
        private String reason;//成功或失败的原因,服务器靠不住，不能显示服务器的原因。还是要自己判断显示原因。
        private String data;//对应实体类

        private String code;
        private String errmsg;//错误信息
        private String list;


        private String successCode = "0";//成功标志。

        //判断是否成功(与服务器格式相对应)
        public boolean isSuccess() {
//            if (result != null && result.trim().equals(successCode)) {
//                return true;
//            }
            if (code != null && code.trim().equals(successCode)) {
                return true;
            }
            return false;
        }

        //判断是否有数据【转化成实体类的数据】
        public boolean isData() {
            if (getData() != null && !getData().equals("") && !getData().equals("null")) {
                return true;
            }
            return false;
        }

        //获取实体类数据，统一使用这个。
        public String getData() {
            if (data == null) {
                return list;
            }
            return data;
        }

        //判断是否有Result结果是否有数据
        public boolean isResult() {
            if (getResult() != null && !getResult().equals("") && !getResult().equals("null")) {
                return true;
            }
            return false;
        }


        //判断是否有原因
        public boolean isReason() {
            if (getReason() != null && !getReason().equals("") && !getReason().equals("null")) {
                return true;
            }
            return false;
        }

        public String getResult() {
            return result;
        }

        public void setResult(String result) {
            this.result = result;
        }

        public String getReason() {
            return reason;
        }

        public void setReason(String reason) {
            this.reason = reason;
        }

        public void setData(String data) {
            this.data = data;
        }


        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getErrmsg() {
            return errmsg;
        }

        public void setErrmsg(String errmsg) {
            this.errmsg = errmsg;
        }

        public String getList() {
            return list;
        }

        public void setList(String list) {
            this.list = list;
        }

        public String getSuccessCode() {
            return successCode;
        }

        public void setSuccessCode(String successCode) {
            this.successCode = successCode;
        }

        @Override
        public String toString() {
            //return super.toString();
            //return "successCode\t"+"result:\t" + result + "\treason:\t" + reason + "\tdata:\t" + data;
            return "successCode\t" + successCode + "\tcode:\t" + code + "\terrmsg:\t" + errmsg + "\tlist:\t" + list;
        }
    }


    //回调接口【注意回调方法，全在子线程中，不在UI主线程中。BaseDialog的初始化和isisFinishing都已经做了处理。可以子线程中使用。】
    //泛型必须指明，如果泛型为String或Object类型，则返回服务器原始的数据。
    public static abstract class RequestCallBack<T> {

        String url;

        //这个不用管，在send()方法里会自动赋值调用。
        public void setUrl(String url) {
            this.url = url;
        }

        boolean isCacle = false;//是否缓存，默认不缓存

        public RequestCallBack setCacle(boolean cacle) {
            isCacle = cacle;
            return this;
        }

        boolean isLoading = true;//是否显示加载进度条，默认显示(传入的Activity如果不为空。就显示)

        public RequestCallBack setLoading(boolean loading) {
            isLoading = loading;
            return this;
        }


        boolean isError = true;//是否显示网络异常信息。默认显示，在onFailure()显示

        public RequestCallBack setError(boolean error) {
            isError = error;
            return this;
        }

        boolean isList = false;//是否返回实体类List数组,默认为false

        public RequestCallBack setList(boolean list) {
            isList = list;
            return this;
        }

        //JSON数据缓存标志【在此 统 一 一 下】，可以在onSuccess()或onFailure()方法里。调用此方法。获取缓存标志。可以自己手动调用该方法。
        public String getCacleMark() {
            return url;//需要自己根据实际情况重写，自定义标志。默认就是网络url
        }


        //是否缓存,默认不缓存【可以重写，也可以通过set方法控制。当然重写优先级高。】
        public boolean isCacle() {
            return isCacle;
        }

        //是否显示网络加载进度条
        //onStart()里面调用
        public boolean isLoading() {
            return isLoading;
        }

        //服务器连接失败(一般都是网络异常)
        //onFailure()里面调用
        //onFinish()最后数据获取失败，也会调用
        //onSuccess()服务器数据获取为空时也会调用。
        public boolean isError() {
            return isError;
        }

        //显示错误信息
        public void showError(Activity activity, final String error) {
            if (activity != null && !activity.isFinishing()) {
                //ToastUtils.show(activity, error);
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ToastUtils.showToastView(error);
                    }
                });

            }
        }

        //显示加载弹窗
        public void showLoading(Activity activity) {
            if (activity != null && !activity.isFinishing()) {
                ProgressDailog.buider(activity);
            }

        }

        //关闭加载弹窗
        public static void dismissLoading(Activity activity) {
            if (activity != null && !activity.isFinishing()) {
                ProgressDailog.tear(activity);
            }
        }

        Class clazz;//泛型的具体类型
        boolean isString = false;//是否返回String类型【返回服务器原始的数据，baseBean.setResult("保存了服务器的原始数据")】。默认为false

        //true 已经本地处理，不会再访问网络，false继续访问网络
        //默认就是false继续访问。可以在此开启进度弹窗。ProgressDailog.buider(activity);这个方法会自动跳转到主线程，可以再子线程中调用。
        public boolean onStart(Activity activity) {
            if (isLoading()) {
                showLoading(activity);//开启弹窗
            }
            try {
                //必须指明具体的泛型，不然以下方法出错。就算是默认Object类型也要手动指明。
                ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();//获取当前new对象的泛型的父类类型
                clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0];
                //Log.e("test", "" + Object.class.getName() + "\t" + clazz.getName());
                if (clazz.getName().trim().equals(String.class.getName()) || clazz.getName().trim().equals(Object.class.getName())) {
                    isString = true;
                }
            } catch (Exception e) {
                isString = true;
                Log.e("test", "UtilHttp泛型解析出错:\t" + e.getMessage());
            }
            return false;
        }

        //成功【可以在此缓存网络JSON数据,并传递给onFinish()】
        public BaseBean onSuccess(Activity activity, String result) {
            BaseBean baseBean = null;
            try {
//                Log.e("test", "成功:\t" + result);
                if (result != null && !result.trim().equals("") && !result.trim().equals("null")) {
                    if (isString) {
                        baseBean = new BaseBean();
                        baseBean.setResult(result);//保持服务器返回的原始数据
                    } else {
                        try {
                            JSONObject jsonObject = new JSONObject(result);//服务器返回的格式如果不是JSON格式的数据，就会报错。一般都是JSON
                            baseBean = (BaseBean) JSONObjectUtils.getInstance().getBean(jsonObject, BaseBean.class);
                        } catch (Exception e) {
                            //自动转换成JSON数据，存储倒list字段里。
                            JSONArray jsonArray = new JSONArray(result);
                            baseBean = new BaseBean();
                            baseBean.setList(jsonArray.toString());
                            //数组里的数据，默认标准成功。
                            baseBean.setCode(baseBean.successCode);
                            baseBean.setReason(baseBean.successCode);
                            Log.e("test", "JSONObject转换异常:\t" + e.getMessage());
                        } finally {

                        }
                    }
                    if (isCacle()) {
                        //缓存【缓存原始数据】
                        CacheUtils.getInstance().put(getCacleMark(), result);
                    }
                }
            } catch (Exception e) {
                Log.e("test", "异常链接:\t" + url);
                Log.e("test", "UtilHttp onSuccess异常\t" + e.getMessage() + "\tresult:\t" + result);
            }
            if (result == null || result.trim().equals("") || result.trim().equals("null")) {
                if (isError()) {//服务器什么数据都没有返回。
                    showError(activity, "呀，服务器开小差了");
                }
            }
            return baseBean;
        }

        //访问失败【基本可以断定网络无法连接】
        //可以在此读取缓存JSON数据,并传递给onFinish()
        public BaseBean onFailure(Activity activity, String result) {
//            Log.e("test","失败:\t"+result);
            BaseBean baseBean = null;
            try {
                if (isCacle()) {
                    //读取缓存【原始数据】
                    String result2 = CacheUtils.getInstance().getAsString(getCacleMark());
                    if (result2 != null && !result2.trim().equals("") && !result2.trim().equals("null")) {
                        try {
                            JSONObject jsonObject = new JSONObject(result2);
                            baseBean = (BaseBean) JSONObjectUtils.getInstance().getBean(jsonObject, BaseBean.class);
                        } catch (Exception e) {
                            Log.e("test", "缓存JSON数据解析异常:\t" + e.getMessage());
                            try {
                                baseBean=new BaseBean();
                                baseBean.setResult(result2);//将原始数据保存到Result里面。
                                isString=true;//数据格式为字符串。
                                //Log.e("test","缓存原始数据:\t"+result2);
                            } catch (Exception e2) {
                                Log.e("test", "缓存JSON数据解析异常2:\t" + e2.getMessage());
                            }
                        }

                    }
                }
            } catch (Exception e) {
                Log.e("test", "UtilHttp onFailure异常\t" + e.getMessage() + "\tresult:\t" + result);
            }
            if (isError()) {
                showError(activity, "呀，网络开小差了");
            }
            return baseBean;
        }

        /**
         * 一个BaseBean实体类，不可能适应所有服务器格式。服务器换了，BaseBean必须相应的要变化，不可能不变。
         * 获取要转化成实体类的数据，可以重写【防止BaseBean不符合服务器格式，而进行扩展】
         *
         * @return
         */
        public String getData(BaseBean baseBean) {
            if (baseBean != null && baseBean.isData()) {
                return baseBean.getData();
            }
            return null;
        }

        //不管是成功还是失败，最后都会在执行。判断BaseBean是否为空，并对数据进行解析。实例化时，可以重写该方法。
        //可以用来关闭进度弹窗。ProgressDailog.tear(activity);这个方法会自动跳转到主线程，可以再子线程中调用。
        public void onFinish(Activity activity, final BaseBean baseBean) {
//            Log.e("test", "完成:\t" + baseBean);
            if (isLoading()) {
                dismissLoading(activity);//关闭弹窗
            }
            if (baseBean == null) {
                return;
            }
            try {
                if (isString || baseBean.getClass().getName().equals(clazz.getName())) {
                    if (activity != null && !activity.isFinishing()) {
                        activity.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {

                                //String和Object类型也是返回BaseBean类型
                                //其中baseBean.setResult("保存了服务器的原始数据")

                                baseBean(baseBean);//返回String类型和BaseBean类型,不会调用finish()方法。
                            }
                        });
                    } else {
                        baseBean(baseBean);//返回String类型和BaseBean类型,不会调用finish()方法。
                    }
                    return;
                }
            } catch (Exception e) {
                Log.e("test", "UtilHttp onFinish异常:\t" + e.getMessage());
            }
            try {
                String data = getData(baseBean);//获取实体类数据
                //baseBean(baseBean)返回true,并且baseBean.isData()数据不为空，才会返回具体的实体类。
                if (baseBean(baseBean) && data != null) {//判断服务器是否有实体类的数据返回

                    if (isList()) {
                        //返回实体类List数组
                        JSONArray jsonArray = new JSONArray(data);
                        final List<T> list = JSONObjectUtils.getInstance().getArrayList(jsonArray, clazz);
                        if (list != null) {
                            if (activity != null && !activity.isFinishing()) {
                                activity.runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        finish(list);//根据泛型，返回具体的实体类List数组。
                                    }
                                });
                            } else {
                                finish(list);//根据泛型，返回具体的实体类List数组。
                            }
                            return;//返回实体类List数组
                        }
                    } else {
                        //返回单个实体类
                        JSONObject jsonObject = new JSONObject(data);
                        final T t = (T) JSONObjectUtils.getInstance().getBean(jsonObject, clazz);
                        if (t != null) {
                            if (activity != null && !activity.isFinishing()) {
                                activity.runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        finish(t);//根据泛型，返回具体的实体类。
                                    }
                                });
                            } else {
                                finish(t);//根据泛型，返回具体的实体类。
                            }
                            return;//返回实体类
                        }
                    }
                }
            } catch (Exception e) {
                Log.e("test", "UtilHttp onFinish异常2:\t" + e.getMessage());
            }
        }

        //一般不需要重写上面的方法(基本逻辑都已实现)，只要实现下面的方法即可。
        // 【默认情况下，只需要实现baseBean()和finish()即可，如果要整个流程，可以直接重写onStart(),onFinish()等方法】
        //以下方法，不需要判断参数是否为空(null),为空的话不会调用以下方法。
        //如果Activity不为空，以下方法会在主线程中回调。

        //只要baseBean不为空，就一定会调用。【为空不会调用】
        public boolean baseBean(BaseBean baseBean) {
            return false;
        }

        //baseBean(baseBean)返回true,并且baseBean.isData()数据不为空，且T不为空才会调用
        public void finish(T t) {
        }

        //是否返回实体类数组
        public boolean isList() {
            return isList;//可以重写，也可以通过设置isList的值来控制(重写优先级要大)。默认为false
        }

        //isList()返回为true时调用
        public void finish(List<T> list) {
        }

    }

    /**
     * 兼容之前的简易版本Get
     *
     * @param url
     * @param params
     * @param RequestCallBack
     */
    public void sendGet(final String url, final Map<String, String> params, final RequestCallBack RequestCallBack) {
        sendGet(null, url, null, params, RequestCallBack);
    }

    /**
     * Get请求【使用了线程池，无论UI主线程还是后台线程都可执行】
     *
     * @param url             发送请求的 URL 如：String url="http://www.app386.com/app-mm/activity/adraddac";
     * @param header          头部
     * @param params          请求参数 Map集合(方法内部会自动拼接成 name1=value1&name2=value2 的字符串形式)
     * @param RequestCallBack 回调接口
     */
    public void sendGet(final Activity activity, final String url, final Map<String, String> header, final Map<String, String> params, final RequestCallBack RequestCallBack) {
        if (RequestCallBack != null && url == null) {
            RequestCallBack.onFailure(activity, "url为空");
            return;
        }
        RequestCallBack.setUrl(url);
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                if (RequestCallBack != null) {
                    if (RequestCallBack.onStart(activity)) {
                        return;//返回true,本地已经处理。不需再进行网络访问。
                    }
                }
                String result = "";
                BufferedReader in = null;
                String urlNameString = url;
                if (params != null && params.size() > 0) {
                    StringBuffer sb = new StringBuffer();
                    for (Map.Entry<String, String> e : params.entrySet()) {
                        sb.append(e.getKey());
                        sb.append("=");
                        sb.append(e.getValue());
                        sb.append("&");
                    }
                    urlNameString = urlNameString + "?" + sb.substring(0, sb.length() - 1);//Get传值，其实也是params，都在body里面
                }
                try {
                    URL realUrl = new URL(urlNameString);
                    // 打开和URL之间的连接
                    URLConnection connection = realUrl.openConnection();
                    //超时设置
                    connection.setConnectTimeout(timeOut);//设置连接主机超时（单位：毫秒）。时间设置绝对有效。前提：手机开机之后至少必须连接一次网络，其后再断网。都有效。如果手机开机就没有网络,则设置无效。
                    connection.setReadTimeout(0);//设置从主机读取数据超时（单位：毫秒）。默认就是0。实际的连接超时时间的 ConnectTimeout+ReadTimeout
                    // 设置通用的请求属性
                    connection.setRequestProperty("accept", "*/*");
                    connection.setRequestProperty("connection", "Keep-Alive");
                    connection.setRequestProperty("user-agent",
                            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

                    if (header != null && header.size() > 0) {
                        for (Map.Entry<String, String> e : header.entrySet()) {
                            connection.setRequestProperty(e.getKey(), e.getValue());
                            //Log.e("test", "键：\t" + e.getKey() + "\t值：\t" + e.getValue());
                        }
                    }
                    //Log.e("test","output:\t"+connection.getDoOutput()+"\tinput:\t"+connection.getDoInput());
                    // Get设置如下。
                    // POST两个都必须是true。Get不能。Get必须设置如下。默认就是如下。
                    connection.setDoOutput(false);//不允许写
                    connection.setDoInput(true);//只允许读

                    // 建立实际的连接
                    connection.connect();
                    // 获取所有响应头字段
                    //Map<String, List<String>> map = connection.getHeaderFields();
                    // 遍历所有的响应头字段
                    //for (String key : map.keySet()) {
                    //   System.out.println(key + "--->" + map.get(key));
                    //}
                    // 定义 BufferedReader输入流来读取URL的响应
                    in = new BufferedReader(new InputStreamReader(
                            connection.getInputStream()));
                    String line;
                    while ((line = in.readLine()) != null) {
                        result += line;
                    }
                    // 使用finally块来关闭输入流
                    try {
                        if (in != null) {
                            in.close();
                        }
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    }
                    try {
                        if (RequestCallBack != null) {
                            RequestCallBack.onFinish(activity, RequestCallBack.onSuccess(activity, result));
                        }
                    } catch (Exception e) {
                        Log.e("test", "get回调异常:\t" + e.getMessage());
                    }

                } catch (Exception e) {
                    if (RequestCallBack != null) {
                        RequestCallBack.onFinish(activity, RequestCallBack.onFailure(activity, e.getMessage()));
                    }
                }
            }
        });
    }


    /**
     * 兼容之前的简易版本Post
     *
     * @param url
     * @param params
     * @param RequestCallBack
     */
    public void sendPost(final String url, final Map<String, String> params, final RequestCallBack RequestCallBack) {
        sendPost(null, url, null, params, null, RequestCallBack);
    }

    /**
     * Post请求【使用了线程池，无论UI主线程还是后台线程都可执行】
     *
     * @param url             发送请求的 URL 如：String url="http://www.app386.com/app-mm/activity/adraddac";
     * @param header          头部参数请求，一般为null
     * @param params          请求参数 Map集合(方法内部会自动拼接成 name1=value1&name2=value2 的字符串形式)
     * @param body            params为null才会有效，body是一个内容整体，params其实都是放在body里面的。param是body的子集。
     * @param RequestCallBack 回调接口
     */
    public void sendPost(final Activity activity, final String url, final Map<String, String> header, final Map<String, String> params, final String body, final RequestCallBack RequestCallBack) {

        if (RequestCallBack != null && url == null) {
            RequestCallBack.onFailure(activity, "url为空");
            return;
        }
        RequestCallBack.setUrl(url);
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                if (RequestCallBack != null) {
                    if (RequestCallBack.onStart(activity)) {
                        return;
                    }
                }
                try {
                    PrintWriter out = null;
                    BufferedReader in = null;
                    String result = "";
                    URL realUrl = new URL(url);
                    // 打开和URL之间的连接
                    URLConnection conn = realUrl.openConnection();
                    conn.setConnectTimeout(timeOut);//超时连接，超过这个时间还没连接上，就会连接失败
                    conn.setReadTimeout(0);
                    // 设置通用的请求属性,conn.setRequestProperty就是Header部分。
                    conn.setRequestProperty("accept", "*/*");
                    conn.setRequestProperty("connection", "Keep-Alive");
                    conn.setRequestProperty("user-agent",
                            "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

                    if (header != null && header.size() > 0) {
                        for (Map.Entry<String, String> e : header.entrySet()) {
                            conn.setRequestProperty(e.getKey(), e.getValue());
                            //Log.e("test","键：\t"+e.getKey()+"\t值：\t"+e.getValue());
                        }
                    }

                    // 发送POST请求必须设置如下两行
                    conn.setDoOutput(true);
                    conn.setDoInput(true);

                    // 获取URLConnection对象对应的输出流
                    out = new PrintWriter(conn.getOutputStream());
                    // 发送请求参数
                    if (params != null && params.size() > 0) {
                        StringBuffer sb = new StringBuffer();
                        for (Map.Entry<String, String> e : params.entrySet()) {
                            sb.append(e.getKey());
                            sb.append("=");
                            sb.append(e.getValue());
                            sb.append("&");
                            //Log.e("test","键：\t"+e.getKey()+"\t值：\t"+e.getValue());
                        }
                        out.print(sb.substring(0, sb.length() - 1));//params在body里面。
                    } else {
                        if (body != null) {
                            out.print(body);//out整体就是一个body
                        }
                    }
                    // flush输出流的缓冲
                    out.flush();
                    // 定义BufferedReader输入流来读取URL的响应
                    in = new BufferedReader(
                            new InputStreamReader(conn.getInputStream(), "UTF-8"));
                    String line;
                    while ((line = in.readLine()) != null) {
                        result += line;
                    }
                    try {
                        if (out != null) {
                            out.close();
                        }
                        if (in != null) {
                            in.close();
                        }
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    try {
                        if (RequestCallBack != null) {
                            RequestCallBack.onFinish(activity, RequestCallBack.onSuccess(activity, result));
                        }
                    } catch (Exception e) {
                        Log.e("test", "Post回调异常:\t" + e.getMessage());
                    }
                } catch (Exception e) {
                    if (RequestCallBack != null) {
                        RequestCallBack.onFinish(activity, RequestCallBack.onFailure(activity, e.getMessage()));
                    }
                }
            }
        });
    }

    /**
     * 兼容之前的简易版本Post文件提交
     *
     * @param url
     * @param params
     * @param files
     * @param RequestCallBack
     */
    public void sendPostFile(final String url, final Map<String, String> params, final Map<String, File> files, final RequestCallBack RequestCallBack) {
        sendPostFile(null, url, null, params, files, RequestCallBack);
    }

    /**
     * 通过拼接的方式构造请求内容，实现参数传输以及文件传输(纯java文件上传，不需要任何第三方)
     * <p>
     * 一般图片上传，只能通过params，而不能使用body
     *
     * @param url             服务器地址 如：String url="http://www.app386.com/app-mm/activity/adraddac";
     * @param header          头部参数请求，一般为null
     * @param params          文本字符串参数
     * @param files           文件类型参数。 key 服务器参数名,value 是文件File类型
     * @param RequestCallBack 回调函数
     */
    public void sendPostFile(final Activity activity, final String url, final Map<String, String> header, final Map<String, String> params, final Map<String, File> files, final RequestCallBack RequestCallBack) {
        if (RequestCallBack != null && url == null) {
            RequestCallBack.onFailure(activity, "url为空");
            return;
        }
        RequestCallBack.setUrl(url);
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                if (RequestCallBack != null) {
                    if (RequestCallBack.onStart(activity)) {
                        return;
                    }
                }
                try {
                    String BOUNDARY = UUID.randomUUID().toString();
                    String PREFIX = "--", LINEND = "\r\n";
                    String MULTIPART_FROM_DATA = "multipart/form-data";
                    String CHARSET = "UTF-8";
                    URL uri = new URL(url);
                    //android 6.0(23)淘汰的是 HttpClient。HttpURLConnection是纯java的。是可以使用的。不需要任何第三方包。
                    HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
                    //conn.setReadTimeout(10 * 1000); // 缓存的最长时间
                    conn.setConnectTimeout(timeOut);//超时连接，超过这个时间还没连接上，就会连接失败
                    conn.setReadTimeout(0);
                    conn.setDoInput(true);// 允许输入
                    conn.setDoOutput(true);// 允许输出
                    conn.setUseCaches(false); // 不允许使用缓存
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("connection", "keep-alive");
                    conn.setRequestProperty("Charsert", CHARSET);
                    conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);

                    if (header != null && header.size() > 0) {
                        for (Map.Entry<String, String> e : header.entrySet()) {
                            conn.setRequestProperty(e.getKey(), e.getValue());
                            //Log.e("test","键：\t"+e.getKey()+"\t值：\t"+e.getValue());
                        }
                    }

                    // 首先组拼文本类型的参数
                    StringBuilder sb = new StringBuilder();
                    if (params != null) {
                        for (Map.Entry<String, String> entry : params.entrySet()) {
                            sb.append(PREFIX);
                            sb.append(BOUNDARY);
                            sb.append(LINEND);
                            sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);//键
                            sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND);
                            sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
                            sb.append(LINEND);
                            sb.append(entry.getValue());//值
                            sb.append(LINEND);
                        }
                    }
                    DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
                    if (params != null && params.size() > 0) {
                        outStream.write(sb.toString().getBytes());
                    }
                    // 发送文件数据
                    if (files != null && files.size() > 0)
                        for (Map.Entry<String, File> file : files.entrySet()) {
                            if (file.getValue() == null || file.getValue().toString().trim().equals("") || file.getValue().toString().trim().equals("null")) {
                                continue;
                            }
                            StringBuilder sb1 = new StringBuilder();
                            sb1.append(PREFIX);
                            sb1.append(BOUNDARY);
                            sb1.append(LINEND);
//                            以下这个uploadfile是固定死的。必须和服务器的参数对于上。
//                            sb1.append("Content-Disposition: form-data; name=\"uploadfile\"; filename=\""
//                                    + file.getValue().getName() + "\"" + LINEND);

                            //以下参数已变成活的。file.getKey().trim()对于服务器上的参数。
                            sb1.append("Content-Disposition: form-data; name=" + file.getKey().trim() + "; filename=\""
                                    + file.getValue().getName() + "\"" + LINEND);
                            //file.getKey()
                            //Log.e("test", "循环图片:\t" + file.getValue().getName() + "\t参数名称:\t" + file.getKey());
                            sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND);
                            sb1.append(LINEND);
                            outStream.write(sb1.toString().getBytes());
                            InputStream is = new FileInputStream(file.getValue());
                            byte[] buffer = new byte[1024];
                            int len = 0;
                            while ((len = is.read(buffer)) != -1) {
                                outStream.write(buffer, 0, len);
                            }
                            is.close();
                            outStream.write(LINEND.getBytes());
                        }
                    // 请求结束标志
                    byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();
                    outStream.write(end_data);
                    outStream.flush();
                    // 得到响应码
                    int res = conn.getResponseCode();

                    // 定义BufferedReader输入流来读取URL的响应
                    BufferedReader in = new BufferedReader(
                            new InputStreamReader(conn.getInputStream(), CHARSET));//解决中文乱码。
                    String line;
                    String result = "";
                    if (res == 200) {
                        while ((line = in.readLine()) != null) {
                            result += line;
                        }
                    }
                    outStream.close();
                    conn.disconnect();
                    //String result = sb2.toString();
                    //result = new String(result.getBytes("iso-8859-1"), CHARSET);//这个可以解决中文乱码。以上方法以及解决了乱码问题。这个不用了。
                    try {
                        if (RequestCallBack != null) {
                            RequestCallBack.onFinish(activity, RequestCallBack.onSuccess(activity, result));
                        }
                    } catch (Exception e) {
                        Log.e("test", "postFile回调异常:\t" + e.getMessage());
                    }
                } catch (Exception e) {
                    if (RequestCallBack != null) {
                        //DNS = false;
                        RequestCallBack.onFinish(activity, RequestCallBack.onFailure(activity, e.getMessage()));
                    }
                }
            }
        });
    }

    //获取内网IP4地址【绝对可行】
    public String getHostIP4() {

        String hostIp = null;
        try {
            Enumeration nis = NetworkInterface.getNetworkInterfaces();
            InetAddress ia = null;
            while (nis.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) nis.nextElement();
                Enumeration<InetAddress> ias = ni.getInetAddresses();
                while (ias.hasMoreElements()) {
                    ia = ias.nextElement();
                    if (ia instanceof Inet6Address) {
                        continue;// skip ipv6,跳过IP6地址
                    }
                    String ip = ia.getHostAddress();
                    if (!"127.0.0.1".equals(ip)) {
                        hostIp = ia.getHostAddress();
                        break;
                    }
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
            Log.e("test", "IP获取异常:\t" + e.getMessage());
        }
        return hostIp;

    }

    //获取外网的IP(要访问Url，要放到后台线程里处理)
    public String getNetIp() {
        URL infoUrl = null;
        InputStream inStream = null;
        String ipLine = "";
        HttpURLConnection httpConnection = null;
        try {
            //infoUrl = new URL("http://ip168.com/");
            infoUrl = new URL("http://pv.sohu.com/cityjson?ie=utf-8");
            URLConnection connection = infoUrl.openConnection();
            httpConnection = (HttpURLConnection) connection;
            int responseCode = httpConnection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                inStream = httpConnection.getInputStream();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(inStream, "utf-8"));
                StringBuilder strber = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    strber.append(line + "\n");
                }
                Pattern pattern = Pattern
                        .compile("((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))");
                Matcher matcher = pattern.matcher(strber.toString());
                if (matcher.find()) {
                    ipLine = matcher.group();
                }
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inStream.close();
                httpConnection.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        //Log.e("test", ipLine);
        return ipLine;
    }

    //不需要任何权限。最保险。
    //获得独一无二的Psuedo ID【如：ffffffff-80ac-a8f1-ffff-ffff8e4712be】,等价于设备号ID【设备唯一标识】
    public String getUniquePsuedoID() {
        String serial = null;

        String m_szDevIDShort = "Psuedo ID:" +
                Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +

                Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +

                Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +

                Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +

                Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +

                Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +

                Build.USER.length() % 10; //13 位

        try {
            serial = Build.class.getField("SERIAL").get(null).toString();
            //API>=9 使用serial号
            return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
        } catch (Exception exception) {
            //serial需要一个初始化
            serial = "serial"; // 随便一个初始化
        }
        //使用硬件信息拼凑出来的15位号码
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    }

    //生成订单号【确保每次生成都不一样】
    public String getSecurityRandom() {
        final int offset = 123456; // offset为固定值，避免被猜到种子来源（和密码学中的加salt有点类似）
        long seed = System.currentTimeMillis() + offset;
        SecureRandom secureRandom1;
        byte[] bytes = new byte[48];
        try {
            secureRandom1 = SecureRandom.getInstance("SHA1PRNG");
            secureRandom1.setSeed(seed);
            secureRandom1.nextBytes(bytes);
        } catch (NoSuchAlgorithmException e) {
            Log.e("test", "订单生成失败:\t" + e.getMessage());
        }
        String orderID = bytes.toString();
        //Log.e("test", "订单号：\t" + orderID);
        return orderID;
    }

}
